package com.hibernate.hrm.service.employee;

import com.hibernate.hrm.dao.DepartmentRepository;
import com.hibernate.hrm.dao.OperateLogRepository;
import com.hibernate.hrm.dao.PositionRepository;
import com.hibernate.hrm.dao.UserRepository;
import com.hibernate.hrm.entity.Department;
import com.hibernate.hrm.entity.OperateLog;
import com.hibernate.hrm.entity.Position;
import com.hibernate.hrm.entity.User;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class EmployeeService {

    private final UserRepository userRepository;
    private final DepartmentRepository departmentRepository;
    private final PositionRepository positionRepository;
    private final OperateLogRepository operateLogRepository;
    private final PasswordEncoder passwordEncoder;
    private final HttpServletRequest request;
    @PersistenceContext  // 使用标准JPA注解
    private EntityManager entityManager;
    @Autowired
    private PlatformTransactionManager transactionManager;

    public Page<User> searchEmployees(
            User currentUser,
            Integer departmentId,
            Integer positionId,
            String keyword,
            Pageable pageable) {

        Specification<User> spec = buildSpecification(
                currentUser,
                departmentId,
                positionId,
                keyword
        );

        return userRepository.findAll(spec, pageable);
    }

    private Specification<User> buildSpecification(
            User currentUser,
            Integer departmentId,
            Integer positionId,
            String keyword) {

        // 使用 allOf() 作为起点
        Specification<User> spec = Specification.allOf();

        // 添加角色基础条件
        int roleId = currentUser.getRoleId().getRoleId();
        if (roleId == 3) { // 部门经理
            spec = spec.and((root, query, cb) ->
                    cb.equal(root.get("departmentID"), currentUser.getDepartmentID()));
        } else if (roleId == 4) { // 普通员工
            spec = spec.and((root, query, cb) ->
                    cb.equal(root.get("userId"), currentUser.getUserId()));
        }

        // 添加筛选条件
        if (departmentId != null) {
            spec = spec.and((root, query, cb) ->
                    cb.equal(root.get("departmentID").get("departmentID"), departmentId));
        }

        if (positionId != null) {
            spec = spec.and((root, query, cb) ->
                    cb.equal(root.get("jobPositionID").get("positionId"), positionId));
        }

        if (StringUtils.hasText(keyword)) {
            spec = spec.and((root, query, cb) ->
                    cb.or(
                            cb.like(root.get("realName"), "%" + keyword + "%"),
                            cb.like(root.get("username").as(String.class), "%" + keyword + "%")
                    ));
        }

        // 只查询在职员工
        return spec.and((root, query, cb) -> cb.isTrue(root.get("status")));
    }

    public User getEmployeeById(Integer id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("员工不存在"));
    }

    public boolean hasViewPermission(User currentUser, User targetEmployee) {
        int roleId = currentUser.getRoleId().getRoleId();

        // 系统管理员、老板、人事经理可以查看所有员工
        if (roleId <= 2) {
            return true;
        }

        // 部门经理只能查看本部门员工
        if (roleId == 3) {
            return currentUser.getDepartmentID().getDepartmentID()
                    .equals(targetEmployee.getDepartmentID().getDepartmentID());
        }

        // 普通员工只能查看自己
        return currentUser.getUserId().equals(targetEmployee.getUserId());
    }

    @Transactional
    public void addEmployee(User currentUser, User newEmployee, String rawPassword) {
        // 1. 参数校验
        if (currentUser == null || newEmployee == null || rawPassword == null) {
            throw new IllegalArgumentException("参数不能为空");
        }

        // 2. 验证职位和部门（确保关联实体已持久化）
        Position position = positionRepository.findById(newEmployee.getJobPositionID().getPositionId())
                .orElseThrow(() -> new IllegalArgumentException("职位不存在"));
        Department department = departmentRepository.findById(position.getDepartmentID().getDepartmentID())
                .orElseThrow(() -> new IllegalArgumentException("部门不存在"));

        // 3. 重新设置关联实体（避免游离状态）
        newEmployee.setJobPositionID(position);
        newEmployee.setDepartmentID(department);
        newEmployee.setRoleId(position.getPositionRoleID());

        // 4. 设置自动生成字段
        newEmployee.setUsername(generateEmployeeId(department, newEmployee.getStartingDate()));
        newEmployee.setPassword(passwordEncoder.encode(rawPassword));
        newEmployee.setAge(calculateAge(newEmployee.getBirthday()));
        newEmployee.setCreateTime(LocalDateTime.now());
        newEmployee.setUpdateTime(LocalDateTime.now());
        newEmployee.setStatus(true);

        // 5. 保存员工
        try {
            User savedEmployee = userRepository.save(newEmployee);
            recordAddLog(currentUser,newEmployee);
        } catch (DataIntegrityViolationException e) {
            throw new RuntimeException("违反数据库约束，请检查字段完整性", e);
        }
    }

    // 改进后的ID生成方法，解决并发问题
    private String generateEmployeeId(Department department, LocalDate startingDate) {
        // 加锁或使用数据库序列确保唯一性
        synchronized (this) {
            String datePart = startingDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            long count = userRepository.countByStartingDate(startingDate) + 1;
            return String.format("%s%s%04d",
                    department.getDepartmentCode(),
                    datePart,
                    count);
        }
    }

    private int calculateAge(LocalDate birthday) {
        return Period.between(birthday, LocalDate.now()).getYears();
    }

    private void recordAddLog(User operator,User newEmployee) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("create");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)新增了员工[%s](ID:%d)，职位：%s",
                operator.getRealName(),
                operator.getUserId(),
                newEmployee.getRealName(),
                newEmployee.getUserId(),
                newEmployee.getJobPositionID().getPositionName()
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    @Transactional
    public void updateEmployee(User currentUser, Integer id, User updatedEmployee) {
        User existingEmployee = userRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("员工不存在"));

        // 创建字段修改记录集合
        Set<String> modifiedFields = new HashSet<>();

        // 检查权限
        if (!hasEditPermission(currentUser, existingEmployee)) {
            throw new AccessDeniedException("无权编辑该员工信息");
        }

        // 检查并更新每个字段
        if (!Objects.equals(existingEmployee.getRealName(), updatedEmployee.getRealName())) {
            existingEmployee.setRealName(updatedEmployee.getRealName());
            modifiedFields.add("姓名");
        }
        if (!Objects.equals(existingEmployee.getGender(), updatedEmployee.getGender())) {
            existingEmployee.setGender(updatedEmployee.getGender());
            modifiedFields.add("性别");
        }
        if (!Objects.equals(existingEmployee.getIdNumber(), updatedEmployee.getIdNumber())) {
            existingEmployee.setIdNumber(updatedEmployee.getIdNumber());
            modifiedFields.add("身份证号");
        }
        if (!Objects.equals(existingEmployee.getBirthday(), updatedEmployee.getBirthday())) {
            existingEmployee.setBirthday(updatedEmployee.getBirthday());
            modifiedFields.add("出生日期");
            // 自动计算年龄
            existingEmployee.setAge(calculateAge(updatedEmployee.getBirthday()));
        }
        if (!Objects.equals(existingEmployee.getStartingDate(), updatedEmployee.getStartingDate())) {
            existingEmployee.setStartingDate(updatedEmployee.getStartingDate());
            modifiedFields.add("入职日期");
        }
        if (!Objects.equals(existingEmployee.getEducation(), updatedEmployee.getEducation())) {
            existingEmployee.setEducation(updatedEmployee.getEducation());
            modifiedFields.add("学历");
        }
        if (!Objects.equals(existingEmployee.getMajor(), updatedEmployee.getMajor())) {
            existingEmployee.setMajor(updatedEmployee.getMajor());
            modifiedFields.add("专业");
        }
        if (!Objects.equals(existingEmployee.getEmail(), updatedEmployee.getEmail())) {
            existingEmployee.setEmail(updatedEmployee.getEmail());
            modifiedFields.add("邮箱");
        }
        if (!Objects.equals(existingEmployee.getPhone(), updatedEmployee.getPhone())) {
            existingEmployee.setPhone(updatedEmployee.getPhone());
            modifiedFields.add("电话");
        }

        // 处理关联实体
        if (currentUser.getRoleId().getRoleId() <= 2) {
            if (updatedEmployee.getDepartmentID() != null &&
                    !Objects.equals(existingEmployee.getDepartmentID(), updatedEmployee.getDepartmentID())) {
                Department dept = departmentRepository.findById(
                                updatedEmployee.getDepartmentID().getDepartmentID())
                        .orElse(existingEmployee.getDepartmentID());
                existingEmployee.setDepartmentID(dept);
                modifiedFields.add("部门");
            }

            if (updatedEmployee.getJobPositionID() != null &&
                    !Objects.equals(existingEmployee.getJobPositionID(), updatedEmployee.getJobPositionID())) {
                Position position = positionRepository.findById(
                                updatedEmployee.getJobPositionID().getPositionId())
                        .orElse(existingEmployee.getJobPositionID());
                existingEmployee.setJobPositionID(position);
                existingEmployee.setRoleId(position.getPositionRoleID());
                modifiedFields.add("职位");
            }
        }

        // 如果有字段被修改才保存并记录日志
        if (!modifiedFields.isEmpty()) {
            existingEmployee.setUpdateTime(LocalDateTime.now());
            userRepository.saveAndFlush(existingEmployee);
            recordUpdateLog(currentUser, existingEmployee, modifiedFields);
        }
    }

    private void recordUpdateLog(User operator, User updatedUser, Set<String> modifiedFields) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("update");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)修改了员工[%s](ID:%d)的信息，修改字段: %s",
                operator.getRealName(),
                operator.getUserId(),
                updatedUser.getRealName(),
                updatedUser.getUserId(),
                String.join(", ", modifiedFields)
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1);
        operateLogRepository.save(log);
    }

    @Transactional
    public void deleteEmployee(User currentUser, Integer userIdToDelete) {
        try {
            // 1. 检查是否删除自己
            if (currentUser.getUserId().equals(userIdToDelete)) {
                throw new IllegalArgumentException("不能删除自己的账号");
            }

            // 2. 获取被删除用户信息（用于日志记录）
            User userToDelete = userRepository.findById(userIdToDelete)
                    .orElseThrow(() -> new EntityNotFoundException("用户不存在"));

            // 3. 使用事务模板确保原子性
            TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
            transactionTemplate.execute(status -> {
                // 临时禁用外键约束（仅对当前连接有效）
                entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS = 0").executeUpdate();
                entityManager.flush();

                try {
                    entityManager.createNativeQuery("DELETE FROM operatelog WHERE UserID = ?")
                            .setParameter(1, userIdToDelete)
                            .executeUpdate();
                    entityManager.flush();

                    // 再删除用户
                    userRepository.deleteById(userIdToDelete);
                    entityManager.flush();

                    return null;
                } finally {
                    // 确保恢复约束
                    entityManager.createNativeQuery("SET FOREIGN_KEY_CHECKS = 1").executeUpdate();
                    entityManager.flush();
                }
            });

            // 4. 记录删除日志
            recordDeleteLog(currentUser,userToDelete);

        } catch (Exception e) {
            log.error("删除用户{}失败: {}", userIdToDelete, e.getMessage(), e);
            throw new RuntimeException("删除失败: " + e.getMessage());
        }
    }

    private void recordDeleteLog(User operator,User deletedUser) {
        OperateLog log = new OperateLog();
        log.setUserId(operator.getUserId());
        log.setOperateType("delete");
        log.setOperateDetail(String.format(
                "用户[%s](ID:%d)删除了员工[%s](ID:%d)，原部门：%s，原职位：%s",
                operator.getRealName(),
                operator.getUserId(),
                deletedUser.getRealName(),
                deletedUser.getUserId(),
                deletedUser.getDepartmentID() != null ? deletedUser.getDepartmentID().getDepartmentName() : "无",
                deletedUser.getJobPositionID() != null ? deletedUser.getJobPositionID().getPositionName() : "无"
        ));
        log.setOperateTime(LocalDateTime.now());
        log.setIpAddress(getClientIpAddress());
        log.setStatus(1); // 1表示成功

        operateLogRepository.save(log);
    }

    private String getClientIpAddress() {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public boolean hasEditPermission(User currentUser, User targetEmployee) {
        int roleId = currentUser.getRoleId().getRoleId();

        // 系统管理员、老板、人事经理可以编辑所有员工
        if (roleId <= 2) {
            return true;
        }

        // 部门经理只能编辑本部门员工
        if (roleId == 3) {
            return currentUser.getDepartmentID().getDepartmentID()
                    .equals(targetEmployee.getDepartmentID().getDepartmentID());
        }

        // 普通员工只能编辑自己
        return currentUser.getUserId().equals(targetEmployee.getUserId());
    }
}